package com.dy.yandi.biz.service.requirement;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dy.yandi.api.client.demo.model.entity.DataChartDO;
import com.dy.yandi.api.client.demo.model.vo.DataChartVO;
import com.dy.yandi.api.client.requirement.model.DesignBonus;
import com.dy.yandi.api.client.requirement.model.dto.PerformanceDto;
import com.dy.yandi.api.client.requirement.model.dto.PerformanceVo;
import com.dy.yandi.api.client.requirement.model.dto.VirtualData;
import com.dy.yandi.api.client.requirement.service.DesignBonusService;
import com.dy.yandi.api.constant.DesignConstant;
import com.dy.yandi.api.enums.AeDesignRuleEnum;
import com.dy.yandi.api.enums.DesignEnum;
import com.dy.yandi.api.enums.Ue4DesignRuleEnum;
import com.dy.yandi.biz.dao.pig.DesignBonusMapper;
import com.dy.yandi.biz.service.DataChartService;
import com.google.common.collect.Lists;
import com.pig4cloud.pig.admin.api.dto.SysUserInfo;
import com.pig4cloud.pig.admin.api.entity.SysDictItem;
import com.pig4cloud.pig.admin.api.entity.SysUserTenant;
import com.pig4cloud.pig.admin.api.feign.RemoteDictService;
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
import com.pig4cloud.pig.api.util.JsonUtil;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.util.BigDecimalUtils;
import com.pig4cloud.pig.common.core.util.R;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 绩效奖金表
 * @author  chengang
 * @version  2021-09-28 13:43:52
 * table: design_bonus
 */
@Log4j2
@Service("designBonusService")
@RequiredArgsConstructor
public class DesignBonusServiceImpl extends ServiceImpl<DesignBonusMapper, DesignBonus> implements DesignBonusService {

	private final RemoteUserService remoteUserService;
	private final DataChartService dataChartService;
	private final RemoteDictService remoteDictService;


	//label:需求类型  value:组别ID
	private final String DESIGN_GROUP_DICT_TYPE = "design_group";
	// cost:100 markCost1:10  markCost2:50  out1:15 out2:23
	private final String DESIGN_PERFORMANCE_AE = "design_performance_ae";
	// cost1:50 cost2:100 mark:2  out1:5 out2:3
	private final String DESIGN_PERFORMANCE_UE4 = "design_performance_ue4";

	/**
	 * 周期小于系统时间，各项分值不会变，因规则值会调整，故需要存储历史周期的绩效数据
	 * @param req
	 * @return
	 */
	@Override
	public IPage<PerformanceVo> getPerformance(PerformanceDto req) {
		IPage<PerformanceVo> page = new Page<>();
		Integer type = this.getRequirementType(req.getDeptGroupId());
		if (type == -1) {return page;}

		//没有人直接返回
		if (CollectionUtils.isEmpty(req.getAllUserList())) {return page;}

		//设置需求类型
		req.setType(type);
		//转化时间格式-前端是月维度
		req.setStartDate(localDateToString(getLocalDateMin(req.getStartDate()),"yyyy-MM-dd"));
		req.setEndDate(localDateToString(getLocalDateMax(req.getEndDate()),"yyyy-MM-dd"));

		page.setSize(req.getSize());
		page.setCurrent(req.getCurrent());

		//构建查询时间范围和用户列表的笛卡尔积
		req.setDictSql(getDictSql(req,false));

		page = getBaseMapper().getPerformance(page, req);

		if (CollectionUtils.isNotEmpty(page.getRecords())) {
			List<PerformanceVo> list = page.getRecords();
			//不分页获取当前团队所有人的绩效
			req.setDictSql(getDictSql(req,true));
			// 笛卡尔积的方式可以前端分页的
			req.setSize(-1);
			List<PerformanceVo> listNoPage = getBaseMapper().getPerformanceNoPage(req);

			List<Integer> userIds = list.stream().map(PerformanceVo::getUserId).distinct().collect(Collectors.toList());
			R<List<SysUserInfo>> userInfoList = remoteUserService.getUserInfoByIds(SecurityConstants.FROM_IN, userIds);

			//计算当前团队所有人的绩效
			List<DataChartDO> allCostList = this.getAllMaterialCost(req,listNoPage);
			//设置非团队属性
			this.getCalcResult(list,allCostList,false,null);

			List<PerformanceVo> allScoreList = this.getCalcResult(listNoPage,allCostList,true,null);

			//计算AE团队分,UE4本来就是团队作业故没有团队分 and 计算总分=消耗分+创意分+产量分-综合表现扣分+团队分
			if (CollectionUtils.isNotEmpty(allScoreList)) {
				//按周期分组获取团队绩效
				Map<String,List<PerformanceVo>> periodMap = allScoreList.stream().collect(Collectors.groupingBy(PerformanceVo::getPeriodValue));

				list.forEach(item -> {
					//1.设置用户名、组别名
					if (userInfoList.getCode() == 0) {
						Map<Integer,SysUserInfo> userTenantMap = userInfoList.getData().stream().collect(Collectors.toMap(SysUserInfo::getUserId, Function.identity()));
						SysUserInfo userInfo = userTenantMap.get(item.getUserId());
						item.setUserName(userInfo!=null?userInfo.getUserName():"");
						item.setDeptGroupId(userInfo!=null?userInfo.getDeptGroupId():null);
						item.setDeptGroupName(userInfo!=null?userInfo.getDeptGroupName():"");
					}
					//AE-团队分
					List<PerformanceVo> currentPeriodList = periodMap.get(item.getPeriodValue());
					List<PerformanceVo> currentUser = currentPeriodList.stream().filter(v -> item.getUserId().equals(v.getUserId())).collect(Collectors.toList());
					if (CollectionUtils.isNotEmpty(currentUser) ) {
						PerformanceVo vo = currentUser.get(0);

						if (DesignEnum.DESIGN_TYPE_1.getKey().equals(item.getType())) {
							item.setTeamScore(vo.getTeamScore());
						} else if (DesignEnum.DESIGN_TYPE_2.getKey().equals(item.getType())) {
							//UE4 - 消耗分
							item.setCostScore(vo.getCostScore());
							//UE4 - 月产量分
							item.setOutPutScore(vo.getOutPutScore());
						}
					}
					//总分计算后才能设置评价
					item.setEvaluate(this.calcEvaluate(item.getType(),new BigDecimal(item.getTotalScore())));
					item.setEvaluateName(this.calcEvaluateName(item.getType(),new BigDecimal(item.getTotalScore())));
				});
			}
		}
		return page;
	}


	/**
	 * 获取相差的月、季度、年
	 * @param req
	 * @return
	 */
	private List<String> getDiffTime(PerformanceDto req){
		List<String> result = new ArrayList<>();
		for (LocalDate localDate : getDiffLocalDate(req)) {
			if (DesignConstant.PERIOD_MONTH.equals(req.getPeriod())) {
				result.add(localDateToString(localDate,"yyyyMM"));
			} else if (DesignConstant.PERIOD_QUARTERS.equals(req.getPeriod())) {
				Double quarters = Math.ceil(localDate.getMonthValue() / 3.0);
				String yq = localDate.getYear() + "-" + quarters.intValue();
				if (!result.contains(yq)) {
					result.add(yq);
				}
			} else if (DesignConstant.PERIOD_YEARS.equals(req.getPeriod())) {
				if (!result.contains(String.valueOf(localDate.getYear()))) {
					result.add(String.valueOf(localDate.getYear()));
				}
			}
		}
		return result;
	}

	private List<LocalDate> getDiffLocalDate(PerformanceDto req){
		LocalDate start = getLocalDateMin(req.getStartDate());
		LocalDate end = getLocalDateMin(req.getEndDate());
		Period period = Period.between(start,end);
		List<LocalDate> localDates = new ArrayList<>();
		localDates.add(start);
		for (int i = 1;i <= period.toTotalMonths();i++) {
			localDates.add(start.plusMonths(i));
		}
		return localDates;
	}

	/**
	 * 获取拼接的笛卡尔积SQL
	 * @param req
	 * @return
	 */
	private String getDictSql(PerformanceDto req,boolean isTeam) {
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT su.userId, m.periodValue,  ");

		sb.append(req.getPeriod() +" as period,");
		sb.append(req.getType() +" as type,");
		sb.append("'" +req.getStartDate()+ "'" +" as searchStartDate,");
		sb.append("'" +req.getEndDate()+ "'" +" as searchEndDate");

		if (req.getDeptGroupId() != null) {
			sb.append(", ");
			sb.append(req.getDeptGroupId() +" as searchDeptGroupId");
		}

		sb.append(" FROM (  ");
		List<Integer> allUser = req.getSearchUserList();
		if (isTeam) {
			allUser = req.getAllUserList();
		}
		List<String> diff = getDiffTime(req);
		for (int i = 0;i<allUser.size();i++) {
			if (i == 0) {
				sb.append( "SELECT " + allUser.get(i) +"  as userId " );
			} else {
				sb.append( "UNION SELECT " + allUser.get(i) +"  as userId " );
			}
		}

		sb.append(" ) su, ( ");

		for (int i = 0;i<diff.size();i++) {
			if (i == 0) {
				sb.append( "SELECT " + diff.get(i) +"  as periodValue " );
			} else {
				sb.append( "UNION SELECT " + diff.get(i) +"  as periodValue " );
			}
		}

		sb.append(" ) m ");
		return sb.toString();
	}


	@Override
	public List<PerformanceVo> getPerformanceNoPage(PerformanceDto dto) {
		//构建查询时间范围和用户列表的笛卡尔积
		dto.setDictSql(getDictSql(dto,true));
		return getBaseMapper().getPerformanceNoPage(dto);
	}

	/**
	 * 定时器执行时没有租户需要在任务中指定
	 * @param list
	 * @param costList
	 * @param tenantId  -- 仅定时器执行时有值
	 * @param isTeam
	 * @return
	 */
	@Override
	public List<PerformanceVo> getCalcResult(List<PerformanceVo> list,List<DataChartDO> costList,boolean isTeam,Integer tenantId) {

		//获取字典配置的规则值-单位-元
		List<SysDictItem> dictItemList4AE = new ArrayList<>();
		List<SysDictItem> dictItemList4UE4 = new ArrayList<>();
		if (tenantId == null) {
			dictItemList4AE = this.getDict(DESIGN_PERFORMANCE_AE);
			dictItemList4UE4 = this.getDict(DESIGN_PERFORMANCE_UE4);
		} else {
			dictItemList4AE = this.getDict(DESIGN_PERFORMANCE_AE,tenantId);
			dictItemList4UE4 = this.getDict(DESIGN_PERFORMANCE_UE4,tenantId);
		}

		Map<String,String> dictMap4AE = dictItemList4AE.stream().collect(Collectors.toMap(SysDictItem::getLabel,SysDictItem::getValue));
		Map<String,String> dictMap4UE4 = dictItemList4UE4.stream().collect(Collectors.toMap(SysDictItem::getLabel,SysDictItem::getValue));

		//去重获取所有人
		List<Integer> userIds = list.stream().map(PerformanceVo::getUserId).distinct().collect(Collectors.toList());
		//获取所有人已存在的计算结果或综合分或历史记录
		List<DesignBonus> bonusList = getDesignBonusList(userIds);
		//按周期分组-存储多种类型的（月、季、年）
		Map<String,List<DesignBonus>> bonusMap =  bonusList.stream().collect(Collectors.groupingBy(DesignBonus::getPeriodValue));

		//计算非团队类型的分值
		list.forEach(item -> {
			//素材ID不为空，存在当月没有做需求的情况
			//2.计算消耗分
			//AE
			List<Long> item_materialIds = new ArrayList<>();
			if (StringUtils.isNotBlank(item.getMaterialIds())) {
				item_materialIds = new ArrayList<>(Arrays.asList(item.getMaterialIds().split(",")))
						.stream().filter(i -> StringUtils.isNotBlank(i)).map(v -> Long.valueOf(v)).distinct().collect(Collectors.toList());
			}

			//获取所有创意标记
			List<Integer> item_creativeRemarks = new ArrayList<>();
			if (StringUtils.isNotBlank(item.getCreativeRemarks())) {
				item_creativeRemarks = new ArrayList<>(Arrays.asList(item.getCreativeRemarks().split(",")))
						.stream().map(v -> Integer.parseInt(v)).collect(Collectors.toList());
			}

			//获取当前行突出标记
			List<Integer> item_prominentRemarks = new ArrayList<>();
			if (StringUtils.isNotBlank(item.getProminentRemarks())) {
				item_prominentRemarks = new ArrayList<>(Arrays.asList(item.getProminentRemarks().split(",")))
						.stream().map(v -> Integer.parseInt(v)).collect(Collectors.toList());
			}

			//0.判断是否跨周期，系统时间>当前周期的从design_bonus取历史周期对应的分值
			//按周期比较
			boolean isHistory = this.getIsHistory(item);

			List<DesignBonus> itemBonus = bonusMap.get(item.getPeriodValue());
			List<DesignBonus> filterBonus = new ArrayList<>();
			if (CollectionUtils.isNotEmpty(itemBonus)) {
				filterBonus = itemBonus.stream()
						.filter(v -> v.getPeriodType().equals(item.getPeriod()) && v.getProducerId().equals(Long.valueOf(item.getUserId().longValue())))
						.collect(Collectors.toList());
			}


			//定时器支持补跑历史数据
			if (isHistory && tenantId == null) {

				//取历史数据
//					DesignBonus bonus = this.getDesignBonus(item);
				if (CollectionUtils.isNotEmpty(filterBonus)) {
					DesignBonus bonus = filterBonus.get(0);
					item.setCostScore(bonus.getCostScore().doubleValue());
					item.setOutPutScore(bonus.getOutPutScore().doubleValue());
					item.setCreativeScore(bonus.getCreativeScore().doubleValue());
					item.setTeamScore(bonus.getTeamScore().doubleValue());
					item.setComprehensiveScore(bonus.getComprehensiveScore().doubleValue());
					item.setTotalScore(bonus.getTotalScore().doubleValue());
					item.setTotalBonus(bonus.getTotalBonus().doubleValue());
					item.setBonus(bonus.getBonus().doubleValue());
					item.setEvaluateName(this.calcEvaluateName(item.getType(),bonus.getTotalScore()));
					item.setCalcTime(DateUtil.format(bonus.getCalcTime(),"yyyy-MM-dd HH:mm:ss"));
				} else {
					log.info("===>历史绩效分不存在，周期内没有或者请检查定时器[calcPerformance]");
				}
			} else {
				//历史数据
				if (CollectionUtils.isNotEmpty(filterBonus)) {
					//如果设置综合分或计算了奖金
					DesignBonus bonus = filterBonus.get(0);
					item.setComprehensiveScore(bonus.getComprehensiveScore().doubleValue());
					//总分实时计算--设置综合分时总分非必填
					//item.setTotalScore(bonus.getTotalScore().doubleValue());
					item.setBonus(bonus.getBonus().doubleValue());
					item.setTotalBonus(bonus.getTotalBonus().doubleValue());
					item.setCalcTime(DateUtil.format(bonus.getCalcTime(),"yyyy-MM-dd HH:mm:ss"));
				}
				if (DesignEnum.DESIGN_TYPE_1.getKey().equals(item.getType())) {
					Double costScore = 0.00;
					//先过滤得到当前周期&用户的所有素材消耗
					List<Long> finalItem_materialIds = item_materialIds;
					List<DataChartDO> cost4Ae = costList.stream().filter(cost -> cost.getPeriod().equals(item.getPeriodValue()) && finalItem_materialIds.contains(cost.getMaterialId())).collect(Collectors.toList());
					BigDecimal totalCost = cost4Ae.stream().map(DataChartDO::getCost).reduce(BigDecimal.ZERO, BigDecimal::add);

					Integer dictCost = this.getPerformanceDictValue(DESIGN_PERFORMANCE_AE, DesignConstant.AE_DESIGN_cost,dictMap4AE);
					BigDecimal dictTotalCost = new BigDecimal(dictCost);

					//总消耗 --> 个人总消耗>100W，则制作者积2分，每多100W加1分，依次叠加
					if (totalCost.compareTo(dictTotalCost) == 1) {
						costScore = BigDecimalUtils.add(costScore,2.0);
						Double sub = 0d;
						//字典不能配置为0
						if (dictCost.intValue() > 0) {
							sub = Math.floor(totalCost.subtract(dictTotalCost).doubleValue() / dictCost);
						}
						if (sub >=1) {
							costScore = BigDecimalUtils.add(costScore,sub * 1.0);
						}
					}
					//单素材是否大于规则值 --> 单素材>100W，则再加2分，不叠加
					List<DataChartDO> singleCost4Ae = cost4Ae.stream().filter(cost -> cost.getCost().compareTo(dictTotalCost) == 1).collect(Collectors.toList());
					if (CollectionUtils.isNotEmpty(singleCost4Ae)) {
						costScore = BigDecimalUtils.add(costScore,2.0);
					}

					//3.计算产量分
					Double outPutScore = 0.00;
					Integer dictOut1 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_AE, DesignConstant.AE_DESIGN_out1,dictMap4AE);
					Integer dictOut2 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_AE, DesignConstant.AE_DESIGN_out2,dictMap4AE);
					if (item.getOutPutNum().intValue() >= dictOut2.intValue()) {
						outPutScore = BigDecimalUtils.add(outPutScore,2.0);
					}  else if (item.getOutPutNum().intValue() >= dictOut1.intValue()) {
						outPutScore = BigDecimalUtils.add(outPutScore,1.0);
					}

					//4.计算标志分
					Double creativeScore;
					Integer dictMarkCost1 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_AE, DesignConstant.AE_DESIGN_markCost1,dictMap4AE);
					Integer dictMarkCost2 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_AE, DesignConstant.AE_DESIGN_markCost2,dictMap4AE);
					List<BigDecimal> tempCreativeScore = new ArrayList<>();

					// AE -> 创意标和素材个数和顺序是对应的  UE4 -> SQL中去重了，一个素材对应多个标记
					//Map<素材ID,标记>
					//UE4需求同一个人可以参与多种制作类型故素材ID要去重 SQL中已去重
					Map<Long,Integer> materialCreativeMap = this.listToMap(item_materialIds,item_creativeRemarks);

					//<素材ID,标记>
					materialCreativeMap.forEach((materialId,mark) -> {
						//获取素材的消耗
						if (mark == 1) {
							List<DataChartDO> creativeMaterial = cost4Ae.stream().filter(cost -> cost.getMaterialId().equals(materialId)).collect(Collectors.toList());
							if (CollectionUtils.isNotEmpty(creativeMaterial)) {
								BigDecimal cost = creativeMaterial.get(0).getCost();
								if (cost.compareTo(new BigDecimal(dictMarkCost2)) == 1) {
									tempCreativeScore.add(new BigDecimal(2));
								}  else if (cost.compareTo(new BigDecimal(dictMarkCost1)) == 1) {
									tempCreativeScore.add(new BigDecimal(1));
								}
							}
						}
					});
					creativeScore = tempCreativeScore.stream().reduce(BigDecimal.ZERO, BigDecimal::add).doubleValue();

					//setting
					item.setCostScore(costScore);
					item.setOutPutScore(outPutScore);
					item.setCreativeScore(creativeScore);

				} else if (DesignEnum.DESIGN_TYPE_2.getKey().equals(item.getType())) {
					// UE4
					//2.计算消耗分-参考团队计算
					//3.计算产量分 - 总体月产量（完成的项目数）>=5。且个人参与项目数>=3则个人加1 (总体月产量-参考团队计算)
					//4.计算标志分 - UE4 技术表现分 按个人维度
					//得到有突出标的集合
					Double creativeScore = 0.00;
					// UE4需求 同一个人可以参与多种制作类型
					// UE4同一个人做了不通的制作类型，主管都打标了，只能算一次 DB已去重
					List<Integer> prominentRemarks = item_prominentRemarks.stream().distinct().filter(v -> v == 1).collect(Collectors.toList());
					Integer dictMark = this.getPerformanceDictValue(DESIGN_PERFORMANCE_UE4, DesignConstant.UE4_DESIGN_mark,dictMap4UE4);
					//没标记不计分
					if (prominentRemarks.size() > 0) {
						if (prominentRemarks.size() > dictMark.intValue()) {
							creativeScore = BigDecimalUtils.add(creativeScore,2.0);
						} else {
							creativeScore = BigDecimalUtils.add(creativeScore,1.0);
						}
					}
					item.setCreativeScore(creativeScore);

					//5.计算团队分 - UE4 团队分均是0
				}
			}

		});

		// 计算团队类型的分值
		if (isTeam) {
			//按周期分组获取团队绩效
			Map<String,List<PerformanceVo>> periodMap = list.stream().collect(Collectors.groupingBy(PerformanceVo::getPeriodValue));

			//5.计算团队分 --先要把成员的其他种类分计算完成
			list.forEach(item -> {
				List<PerformanceVo> currentPeriodList = periodMap.get(item.getPeriodValue());
				if (DesignEnum.DESIGN_TYPE_1.getKey().equals(item.getType())) {
					// 若团队成员分数都大于0情况，则全体积分+1分
					// 因为综合分是扣分项，故只能用总分判断
					List<PerformanceVo> middleList = currentPeriodList.stream().filter(vo -> vo.getMiddleScore().compareTo(0.0d) > 0).collect(Collectors.toList());
					if (currentPeriodList.size() == middleList.size()) {
						item.setTeamScore(1.0d);
					}
				} else if (DesignEnum.DESIGN_TYPE_2.getKey().equals(item.getType())) {
					//计算团队当前周期的月产量
					List<String> idsStr = currentPeriodList.stream().map(PerformanceVo::getIds).filter(Objects::nonNull).collect(Collectors.toList());
					List<Long> idsCount = idsStr.stream().map(v -> v.split(",")).flatMap(Arrays::stream).distinct().map(v -> Long.valueOf(v)).collect(Collectors.toList());

					Double costScore = 0.00;
					//团队总消耗
					//得到当前周期的团队总消耗
					List<DataChartDO> cost4Ue4 = costList.stream().filter(cost -> cost.getPeriod().equals(item.getPeriodValue())).collect(Collectors.toList());
					BigDecimal totalCost = cost4Ue4.stream().map(DataChartDO::getCost).reduce(BigDecimal.ZERO, BigDecimal::add);

					//获取字典配置的规则值-单位-元
					Integer dictCost1 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_UE4, DesignConstant.UE4_DESIGN_cost1,dictMap4UE4);
					Integer dictCost2 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_UE4, DesignConstant.UE4_DESIGN_cost2,dictMap4UE4);
					BigDecimal dictTotalCost1 = new BigDecimal(dictCost1);
					BigDecimal dictTotalCost2 = new BigDecimal(dictCost2);

					//总消耗 -->  <=50W，全体3D组-1分  >=100W，全体+1分，每多100W+1分 ,依次叠加
					if (totalCost.compareTo(dictTotalCost1) <= 0) {
						costScore = BigDecimalUtils.sub(costScore,1.0d);

					}
					if (totalCost.compareTo(dictTotalCost2) >= 0) {
						costScore = BigDecimalUtils.add(costScore,1.0);
						Double sub = 0d;
						//字典不能配置为0
						if (dictCost2.intValue() > 0) {
							sub = Math.floor(totalCost.subtract(dictTotalCost2).doubleValue() / dictCost2);
						}
						if (sub >=1) {
							costScore = BigDecimalUtils.add(costScore,sub * 1.0);
						}
					}

					item.setCostScore(costScore);

					//UE4 团队月产量 = idsCount.size()
					Integer dictOut1 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_UE4, DesignConstant.UE4_DESIGN_out1,dictMap4UE4);
					Integer dictOut2 = this.getPerformanceDictValue(DESIGN_PERFORMANCE_UE4, DesignConstant.UE4_DESIGN_out2,dictMap4UE4);
					if (idsCount.size() >= dictOut1.intValue()
							&& item.getOutPutNum().intValue() >= dictOut2.intValue()) {
						item.setOutPutScore(1.0);
					}
				}

			});

		}

		return list;
	}

	public Boolean getIsHistory(PerformanceVo item){
		boolean isHistory = false;
		if (DesignConstant.PERIOD_MONTH.equals(item.getPeriod())) {
			YearMonth startDate = YearMonth.parse(item.getPeriodValue(), DateTimeFormatter.ofPattern("yyyyMM"));
			if (startDate.getMonthValue() < YearMonth.now().getMonthValue()) {
				isHistory = true;
			}
		} else if (DesignConstant.PERIOD_QUARTERS.equals(item.getPeriod())) {
			// 当前时间季度
			Double quarters = Math.ceil(YearMonth.now().getMonthValue() / 3.0);
			String[] period = item.getPeriodValue().split("-");
			if (Integer.parseInt(period[1]) < quarters.intValue()) {
				isHistory = true;
			}
		} else if (DesignConstant.PERIOD_YEARS.equals(item.getPeriod())) {
			if (Integer.parseInt(item.getPeriodValue()) < YearMonth.now().getYear()) {
				isHistory = true;
			}
		}
		return isHistory;
	}

	/**
	 * 获取历史绩效
	 * @param userIds
	 * @return
	 */
	private List<DesignBonus> getDesignBonusList(List<Integer> userIds){
		return getBaseMapper().selectList(Wrappers.<DesignBonus>lambdaQuery()
				.in(DesignBonus::getProducerId,userIds)
				.eq(DesignBonus::getIsDeleted,0));
	}

	/**
	 * 获取历史绩效
	 * @param item
	 * @return
	 */
	private DesignBonus getDesignBonus(PerformanceVo item){
		return getBaseMapper().selectOne(Wrappers.<DesignBonus>lambdaQuery()
				.eq(DesignBonus::getProducerId,item.getUserId())
				.eq(DesignBonus::getPeriodType,item.getPeriod())
				.eq(DesignBonus::getPeriodValue,item.getPeriodValue())
				.eq(DesignBonus::getIsDeleted,0));
	}


	public <K, V> Map<K, V> listToMap(List<K> keys, List<V> values) {
		if (CollectionUtils.isEmpty(keys) || CollectionUtils.isEmpty(values)) {
			return new HashMap<>();
		}
		return keys.stream().collect(Collectors.toMap(key -> key, key -> values.get(keys.indexOf(key)),(k1, k2) -> k1));
	}


	/**
	 * 获取指定集合所有素材的消耗
	 * @param req
	 * @param list
	 * @return
	 */
	@Override
	public List<DataChartDO> getAllMaterialCost(PerformanceDto req, List<PerformanceVo> list){
		DataChartVO chartDto = new DataChartVO();
		//获取当页[组别所有人员]所有的素材ID
		List<String> materialIdStr = list.stream().map(PerformanceVo::getMaterialIds).filter(Objects::nonNull).collect(Collectors.toList());
		List<Long> materialIds = materialIdStr.stream().map(v -> v.split(",")).flatMap(Arrays::stream).distinct().map(v -> Long.valueOf(v)).collect(Collectors.toList());
		if (CollectionUtils.isNotEmpty(materialIds)) {
			//因为规则会调整且需要存储历史周期的各项分值，按天筛选会出现按整月的结果，故前端使用按月区间的控件 yyyy-MM
			LocalDate startDate = LocalDate.parse(req.getStartDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
			LocalDate endDate = LocalDate.parse(req.getEndDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
			chartDto.setStartDate(startDate);
			chartDto.setEndDate(endDate);
			chartDto.setMaterialIds(materialIds);
			return dataChartService.getKpiCost(chartDto);
		} else {
			return new ArrayList<>();
		}
	}

	/**
	 * param = {"time":"2021-09","tenantId":7}
	 * @param period
	 * @param param
	 */
	@Override
	public R getPerformance4Task(Integer period,String param) {
		//定时器计算绩效时按需求类型取所有人的绩效
		// 1:AE 2:UE4
		List<Integer> types = DesignEnum.getAllKey();
		PerformanceDto dto = new PerformanceDto();
		Map<String, Object> paramMap = JsonUtil.parseJSON2Map(param);
		String time;
		if (paramMap.get("tenantId") == null) {
			return R.failed("注意：定时器执行缺少租户ID!");
		}
		if (paramMap.get("time") != null) {
			time = String.valueOf(paramMap.get("time"));
		} else {
			time = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
		}

		dto.setStartDate(localDateToString(getLocalDateMin(time),"yyyy-MM-dd"));
		dto.setEndDate(localDateToString(getLocalDateMax(time),"yyyy-MM-dd"));
		//默认按月
		dto.setPeriod(period);
		//不分页
		dto.setSize(-1);
		Integer tenantId = Integer.parseInt(paramMap.get("tenantId").toString());
		types.forEach(type -> {
			dto.setType(type);
			// 定时器计算绩效 同周期会覆盖,so 需要根据类型找到组别在招组别下所有人，这样才不会覆盖
			// 兼容多个设计部情况 武汉设计部 and 广州设计部
			List<Integer> groupIds = this.getGroupIdByType(type,tenantId);
			if (CollectionUtils.isNotEmpty(groupIds)) {
				groupIds.forEach(groupId -> {
					List<SysUserTenant> userIdList = this.getUserListByGroupId(groupId,tenantId);
					dto.setAllUserList(userIdList.stream().distinct().map(SysUserTenant::getUserId).collect(Collectors.toList()));

					List<PerformanceVo> allUserList = getBaseMapper().getPerformance4Task(dto);
					List<DesignBonus> insertList = Lists.newArrayList();
					if (CollectionUtils.isNotEmpty(allUserList)) {
						//去重获取所有人
						List<Integer> userIds = allUserList.stream().map(PerformanceVo::getUserId).distinct().collect(Collectors.toList());
						//获取所有人已存在的计算结果或综合分或历史记录
						List<DesignBonus> bonusList = this.getDesignBonusList(userIds);
						//按周期分组-存储多种类型的（月、季、年）
						Map<String,List<DesignBonus>> bonusMap =  bonusList.stream().collect(Collectors.groupingBy(DesignBonus::getPeriodValue));

						//计算当前需求类型所有人的绩效-含有所有周期
						List<DataChartDO> allCostList = this.getAllMaterialCost(dto,allUserList);
						//获取计算结果
						List<PerformanceVo> allScoreList = this.getCalcResult(allUserList,allCostList,true,tenantId);
						allScoreList.forEach(item -> {
							List<DesignBonus> itemBonus = bonusMap.get(item.getPeriodValue());
							List<DesignBonus> filterBonus = new ArrayList<>();
							if (CollectionUtils.isNotEmpty(itemBonus)) {
								filterBonus = itemBonus.stream()
										.filter(v -> v.getPeriodType().equals(item.getPeriod()) && v.getProducerId().equals(Long.valueOf(item.getUserId().longValue())))
										.collect(Collectors.toList());
							}
							if (CollectionUtils.isNotEmpty(filterBonus)) {
								DesignBonus value = filterBonus.get(0);
								//周期内更换组别绩效会重新计算
								value.setCostScore(new BigDecimal(item.getCostScore()));
								value.setOutPutScore(new BigDecimal(item.getOutPutScore()));
								value.setCreativeScore(new BigDecimal(item.getCreativeScore()));
								value.setTeamScore(new BigDecimal(item.getTeamScore()));
								value.setTotalScore(new BigDecimal(item.getTotalScore()));
								value.setUpdateId(-1L);
								value.setUpdateTime(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()));
								this.updateById(value);
							} else {
								DesignBonus insert = new DesignBonus();
								insert.setProducerId(item.getUserId().longValue());
								insert.setPeriodType(item.getPeriod());
								insert.setPeriodValue(item.getPeriodValue());
								insert.setCostScore(new BigDecimal(item.getCostScore()));
								insert.setOutPutScore(new BigDecimal(item.getOutPutScore()));
								insert.setCreativeScore(new BigDecimal(item.getCreativeScore()));
								insert.setTeamScore(new BigDecimal(item.getTeamScore()));
								insert.setComprehensiveScore(new BigDecimal(item.getComprehensiveScore()));
								insert.setTotalScore(new BigDecimal(item.getTotalScore()));
								insert.setBonus(new BigDecimal(item.getBonus()));
								insert.setTotalBonus(new BigDecimal(item.getTotalBonus()==null?0.00:item.getTotalBonus()));
								insert.setIsDeleted(0);
								//-1 定时器插入
								insert.setCreateId(-1L);
								insert.setUpdateId(-1L);

								insertList.add(insert);

							}

						});

						if (CollectionUtils.isNotEmpty(insertList)) {
							this.saveBatch(insertList);
						}
					}
				});
			}
		});
		return R.ok();
	}

	@SneakyThrows
	private static LocalDate getLocalDateMin(String time){
		Date date = DateUtil.parse(time,"yyyy-MM");
		LocalDateTime sdt =  LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
		LocalDateTime day = sdt.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
		return day.toLocalDate();
	}

	@SneakyThrows
	private LocalDate getLocalDateMax(String time){
		Date date = DateUtil.parse(time,"yyyy-MM");
		LocalDateTime sdt =  LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
		LocalDateTime day = sdt.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
		return day.toLocalDate();
	}

	private static  String localDateToString(LocalDate date,String pattern){
		DateTimeFormatter fmDate = DateTimeFormatter.ofPattern(pattern);
		return date.format(fmDate);
	}

	private Integer getRequirementType(Integer groupId){
		//默认没有需求--即匹配不到则显示空列表
		Integer type = -1;
		try {
			List<SysDictItem> dictItemList = this.getDict(DESIGN_GROUP_DICT_TYPE);
			if (CollectionUtils.isNotEmpty(dictItemList)) {
				dictItemList = dictItemList.stream().filter(v -> String.valueOf(groupId).equals(v.getValue())).collect(Collectors.toList());
				if (CollectionUtils.isNotEmpty(dictItemList)) {
					type = Integer.parseInt(dictItemList.get(0).getLabel());
				}
			}
		} catch (Exception e) {
			log.info("组别字典表格式配置错误[design_GROUP]");
			e.printStackTrace();
		}
		return type;
	}


	private List<Integer> getGroupIdByType(Integer type,Integer tenantId){
		//默认没有需求--即匹配不到则显示空列表
		List<Integer> groupIds = new ArrayList<>();
		try {
			List<SysDictItem> dictItemList = this.getDict(DESIGN_GROUP_DICT_TYPE,tenantId);
			if (CollectionUtils.isNotEmpty(dictItemList)) {
				dictItemList = dictItemList.stream().filter(v -> String.valueOf(type).equals(v.getLabel())).collect(Collectors.toList());
				if (CollectionUtils.isNotEmpty(dictItemList)) {
//					groupId = Integer.parseInt(dictItemList.get(0).getValue());
					groupIds = dictItemList.stream().map(v -> Integer.parseInt(v.getValue())).collect(Collectors.toList());
				}
			}
		} catch (Exception e) {
			log.info("组别字典表格式配置错误[design_GROUP]");
			e.printStackTrace();
		}
		return groupIds;
	}


	private List<SysUserTenant> getUserListByGroupId(Integer groupId,Integer tenantId){
		R<List<SysUserTenant>> userIdList = remoteUserService.getUserListByGroupIdAndTId(SecurityConstants.FROM_IN,groupId,tenantId);
		if (userIdList.getCode() == 0) {
			return userIdList.getData();
		}
		return Lists.newArrayList();
	}



	private Integer getPerformanceDictValue(String key,String label,Map<String,String> dictItemMap){
		//字典没有配置则返回默认值
		Integer defaultValue = 0;
		if (DESIGN_PERFORMANCE_UE4.equals(key)) {
			defaultValue = Integer.parseInt(Ue4DesignRuleEnum.getValueByLabel(label));
		} else if (DESIGN_PERFORMANCE_AE.equals(key)) {
			defaultValue = Integer.parseInt(AeDesignRuleEnum.getValueByLabel(label));
		}
		if (CollectionUtils.isNotEmpty(dictItemMap)) {
			defaultValue = Integer.parseInt(dictItemMap.get(label));
		}
		return defaultValue;
	}

	private List<SysDictItem> getDict(String type,Integer tenantId){
		R<List<SysDictItem>> dict = remoteDictService.getDictByType4Inner(tenantId,SecurityConstants.FROM_IN,type);
		if (dict != null) {
			if (dict.getCode()==0 && CollectionUtils.isNotEmpty(dict.getData())) {
				return dict.getData();
			}
		}
		return Lists.newArrayList();
	}

	private List<SysDictItem> getDict(String type){
		R<List<SysDictItem>> dict = remoteDictService.getDictByType4Inner(SecurityConstants.FROM_IN,type);
		if (dict != null) {
			if (dict.getCode()==0 && CollectionUtils.isNotEmpty(dict.getData())) {
				return dict.getData();
			}
		}
		return Lists.newArrayList();
	}

	/**
	 * 计算评价显示
	 * @param type
	 * @param totalCore
	 * @return
	 */
	private String calcEvaluateName(Integer type, BigDecimal totalCore){
		List<VirtualData> list = Lists.newArrayList();
		if (DesignEnum.DESIGN_TYPE_1.getKey().equals(type)) {
			list = getVirtualDataList4Ae();
		} else if (DesignEnum.DESIGN_TYPE_2.getKey().equals(type)) {
			list = getVirtualDataList4Ue4();
		}
		int index = getVirtualDataIndex(totalCore,list);
		if (index != -1) {
			VirtualData virtualData = list.get(index);
			return virtualData.getName();
		}
		return "-";
	}

	/**
	 * 计算评价分数占比
	 * @param type
	 * @param totalCore
	 * @return
	 */
	@Override
	public Integer calcEvaluate(Integer type,BigDecimal totalCore){
		List<VirtualData> list = Lists.newArrayList();
		if (DesignEnum.DESIGN_TYPE_1.getKey().equals(type)) {
			list = getVirtualDataList4Ae();
		} else if (DesignEnum.DESIGN_TYPE_2.getKey().equals(type)) {
			list = getVirtualDataList4Ue4();
		}
		int index = getVirtualDataIndex(totalCore,list);
		if (index != -1) {
			VirtualData virtualData = list.get(index);
			return virtualData.getPercentage();
		}
		return 0;
	}

	/**
	 * 评价表示比当前设置的core小
	 * @return
	 */
	private List<VirtualData> getVirtualDataList4Ae(){
		return Lists.newArrayList(
				new VirtualData(BigDecimal.ONE,"不合格",0),
				new VirtualData(new BigDecimal(BigInteger.valueOf(2)),"有待进步",60),
				new VirtualData(new BigDecimal(BigInteger.valueOf(3)),"正常",80),
				new VirtualData(new BigDecimal(BigInteger.valueOf(4)),"良好",90),
				new VirtualData(new BigDecimal(BigInteger.valueOf(6)),"优秀",100),
				new VirtualData(new BigDecimal(BigInteger.valueOf(999999)),"突出贡献",120));
	}

	private List<VirtualData> getVirtualDataList4Ue4(){
		return Lists.newArrayList(
				new VirtualData(BigDecimal.ONE,"不合格",0),
				new VirtualData(new BigDecimal(BigInteger.valueOf(2)),"有待进步",60),
				new VirtualData(new BigDecimal(BigInteger.valueOf(3)),"正常",90),
				new VirtualData(new BigDecimal(BigInteger.valueOf(4)),"良好",100),
				new VirtualData(new BigDecimal(BigInteger.valueOf(999999)),"优秀",120));
	}

	private static int getVirtualDataIndex(BigDecimal totalCore, List<VirtualData> list){
		for (int i = 0; i < list.size(); i++) {
			if (totalCore.compareTo(list.get(i).getCore()) < 0) {
				return i;
			}
		}
		return -1;
	}


	@SneakyThrows
	public static void main(String[] args) {
		List<Long> item_materialIds = new ArrayList<>(Arrays.asList("".split(",")))
				.stream().filter(i -> StringUtils.isNotBlank(i)).map(v -> Long.valueOf(v)).distinct().collect(Collectors.toList());

		List<String> materialIdStr = new ArrayList<>();
		materialIdStr.add("123,234,333");
		materialIdStr.add("123,2,3");
		materialIdStr.add("234,2,3");
		List<Long> materialIds = materialIdStr.stream().map(v -> v.split(",")).flatMap(Arrays::stream).distinct().map(v -> Long.valueOf(v)).collect(Collectors.toList());
//		materialIds.forEach(v ->{
//			System.out.println(v);
//		});

//		YearMonth startDate = YearMonth.parse("202106", DateTimeFormatter.ofPattern("yyyyMM"));

		//LocalDate localDate = startDate.atDay(1).with(TemporalAdjusters.firstDayOfMonth()).with(LocalDate.MIN);
		//Date date = Date.from(startDate.atDay(1).atStartOfDay().toInstant(ZoneOffset.MIN));
		//startDate.with(TemporalAdjusters.firstDayOfMonth()).with(LocalDate.MIN);
		Date date = DateUtil.parse("2021-07","yyyy-MM");
		LocalDateTime sdt =  LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
		LocalDateTime startOfDay = sdt.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
		LocalDateTime endOfDay = sdt.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);

		Double cost = 401.0;
		//向下取整
		System.out.println(Math.floor(cost / 100));

		DecimalFormat df = new DecimalFormat("0.00");
		System.out.println(df.format((0.8/1)));
		float dup = (float)60 / 100;
		float dtp = (float)230 / 100;
		System.out.println(BigDecimalUtils.ceil(BigDecimalUtils.mul(5000d,dup / dtp),2));


		System.out.println(startOfDay.toLocalDate());
		System.out.println(endOfDay.toLocalDate());


		LocalDate start = getLocalDateMin("2021-05-01");
		LocalDate end = getLocalDateMin("2021-05-01");
		Period period = Period.between(start,end);

		System.out.println(period.toTotalMonths());

		List<LocalDate> listML = new ArrayList<>();
		listML.add(start);


//		List<Integer> listM = new ArrayList<>();
//		List<Integer> listY = new ArrayList<>();
//		Integer y =  Integer.parseInt(localDateToString(start,"yyyy"));
//		listY.add(y);
//		for (int i = 1;i <= period.getYears();i++) {
//			listY.add(y+i);
//		}
		for (int i = 1;i <= period.toTotalMonths();i++) {
			listML.add(start.plusMonths(i));
		}
		//根据线相差的月份计算相差的季度
		List<String> monthsList = new ArrayList<>();
		List<String> quartersList = new ArrayList<>();
		List<String> yearsList = new ArrayList<>();
		for (LocalDate localDate : listML) {
			monthsList.add(localDateToString(localDate,"yyyy-MM"));
			Double quarters = Math.ceil(localDate.getMonthValue() / 3.0);
			String yq = localDate.getYear() + "-" + quarters.intValue();
			if (!quartersList.contains(yq)) {
				quartersList.add(yq);
			}
			if (!yearsList.contains(String.valueOf(localDate.getYear()))) {
				yearsList.add(String.valueOf(localDate.getYear()));
			}
		}





		System.out.println(monthsList.toString());
		System.out.println(quartersList.toString());
		System.out.println(yearsList.toString());



	}

}


